<?php defined('BASEPATH') OR exit('No direct script access allowed');

class WS_router extends CI_Router
{
    public function __construct()
    {
        parent::__construct();
    }

    public function custom_validate()
    {
        $class = ucfirst($this->class);
        if (empty($class) OR !file_exists(APPPATH . 'controllers/' . $this->directory . $class . '.php')) {
            if (file_exists(APPPATH . 'config/routesDB.php')) {
                include(APPPATH . 'config/routesDB.php');
                if (!empty($route)) {
                    $uri = implode('/', $this->uri->segments);
                    foreach ($route as $data) {
                        $k = $data['key'];
                        $val = $data['controller'] . $data['value'];
                        $key = str_replace(array(':any', ':num', ':all'), array('[^/]+', '[0-9]+', '.*'), $k);
                        if (preg_match('#^' . $key . '$#', $uri, $matches)) {
                            if (preg_match('/\(:all\)/im', $k)) {
                                preg_match_all('/\(([^)]+\))/imx', $k, $result);
                                if ($result) {
                                    $key_ = array_search('(:all)', $result[0]);
                                    $match_refer = $matches;
                                    array_shift($match_refer);
                                    $match_refer[$key_] = rawurlencode($match_refer[$key_]);
                                    $search = $result = array();
                                    foreach ($match_refer as $kx => $match) {
                                        $kx++;
                                        $search [] = '$' . $kx;
                                        $replace [] = $match;
                                    }
                                    $val = str_replace($search, $replace, $val);
                                }
                            }

                            // Are we using callbacks to process back-references?
                            if (!is_string($val) && is_callable($val)) {
                                // Remove the original string from the matches array.
                                array_shift($matches);

                                // Execute the callback using the values in matches as its parameters.
                                $val = call_user_func_array($val, $matches);
                            } // Are we using the default routing method for back-references?
                            elseif (strpos($val, '$') !== FALSE && strpos($key, '(') !== FALSE) {
                                $val = preg_replace('#^' . $key . '$#', $val, $uri);
                            }
                            $this->_set_request(explode('/', $val));
                            break;
                        }
                    }
                }
            }
        }
    }

    protected function _parse_routes()
    {
        // Turn the segment array into a URI string
        $uri = implode('/', $this->uri->segments);

        // Get HTTP verb
        $http_verb = isset($_SERVER['REQUEST_METHOD']) ? strtolower($_SERVER['REQUEST_METHOD']) : 'cli';

        // Is there a literal match?  If so we're done
        if (isset($this->routes[$uri])) {
            // Check default routes format
            if (is_string($this->routes[$uri])) {
                $this->_set_request(explode('/', $this->routes[$uri]));
                return;
            } // Is there a matching http verb?
            elseif (is_array($this->routes[$uri]) && isset($this->routes[$uri][$http_verb])) {
                $this->_set_request(explode('/', $this->routes[$uri][$http_verb]));
                return;
            }
        }

        // Loop through the route array looking for wildcards
        foreach ($this->routes as $key => $val) {
            // Check if route format is using http verb
            if (is_array($val)) {
                if (isset($val[$http_verb])) {
                    $val = $val[$http_verb];
                } else {
                    continue;
                }
            }

            // Convert wildcards to RegEx
            $key = str_replace(array(':any', ':num', ':all'), array('[^/]+', '[0-9]+', '(.*)'), $key);

            // Does the RegEx match?
            if (preg_match('#^' . $key . '$#', $uri, $matches)) {
                // Are we using callbacks to process back-references?
                if (!is_string($val) && is_callable($val)) {
                    // Remove the original string from the matches array.
                    array_shift($matches);

                    // Execute the callback using the values in matches as its parameters.
                    $val = call_user_func_array($val, $matches);
                } // Are we using the default routing method for back-references?
                elseif (strpos($val, '$') !== FALSE && strpos($key, '(') !== FALSE) {
                    $val = preg_replace('#^' . $key . '$#', $val, $uri);
                }

                $this->_set_request(explode('/', $val));
                return;
            }
        }

        // If we got this far it means we didn't encounter a
        // matching route so we'll set the site default route
        $this->_set_request(array_values($this->uri->segments));
        $this->custom_validate();
    }

    public function _validate_request($segments)
    {
        if ($segments && $segments[0] == $this->config->item('admin_folder')) {
            $this->setRoute('404_override', 'error_404_admin/index');
        }

        $c = count($segments);
        // Loop through our segments and return as soon as a controller
        // is found or when such a directory doesn't exist
        while ($c-- > 0) {
            $test = $this->directory
                . ucfirst($this->translate_uri_dashes === TRUE ? str_replace('-', '_', $segments[0]) : $segments[0]);

            if (!file_exists(APPPATH . 'controllers/' . $test . '.php') && is_dir(APPPATH . 'controllers/' . $this->directory . $segments[0])) {
                $this->set_directory(array_shift($segments), TRUE);
                continue;
            }

            return $segments;
        }

        // This means that all segments were actually directories
        return $segments;
    }

}